// 不带页缓存的oled驱动代码
// 使用方法：
//  1. 准备好软件iic（driver_soft_i2c）或者是硬件i2c的初始化代码（通常是使用图形化工具配置）
//  2. 包装一个i2c写单个字节的函数，这要求跟oled_t结构体内的iic_write_byte_func函数指针的类型一致
//  3. 创建oled_t结构体
//  4. 先调用i2c的初始化函数，后调用oled_init函数来初始化oled
//  5. 使用oled_draw_xxx绘制图像
//  6. 清空图像使用oled_clear

// 参考代码
#if 0
oled_t g_oled = {
    .dev_addr = OLED_DEVICE_DEFAULT_ADDRESS,
    .iic_write_byte_func = IIC_WriteByte
};
    // 初始化iic
    iic_init();
    
    // 延迟保证oled已经开始工作了
    delay_ms(100);
    // 初始化oled
    oled_init(&g_oled);
    // 绘制字符串
    oled_draw_string(&g_oled, 1, 1, "test str");
#endif

// 在MSPM0中使用硬件iic的配置可以参考下面这样
// 程序以MSPM0G3507的外设I2C0连接，在使用之前，一定要先去SysConfig配置好I2C0的外设
// PA10――SDA
// PA11――SCL
// SysConfig配置项目
// 勾选Enable Controller Mode
// 时钟源选择BUSCLK
// Standard Bus Speed选择Fast Mode (400KHz)
// PinMux选择I2C0，引脚选择PA10（I2C0_SDA）和PA11（I2C0_SCL）
// 关于iic写字节的这个函数的实现可以使用下面这个
#if 0
#    define IIC_OLED I2C0

/**IIC指定地址写**
 *@param EquiAddr 从机未移位的原7位地址
 *@param RegAddr 从机寄存器的地址
 *@param Byte 要写入的字节
 **/
static void IIC_WriteByte(uint8_t EquiAddr, uint8_t RegAddr, uint8_t Byte)
{
    uint8_t buff[2] = {RegAddr, Byte};
    DL_I2C_fillControllerTXFIFO(IIC_OLED, buff, 2);
    //填充主机发送缓存区
    while (!(DL_I2C_getControllerStatus(IIC_OLED) & DL_I2C_CONTROLLER_STATUS_IDLE))
        ;
    DL_I2C_startControllerTransfer(IIC_OLED, EquiAddr, DL_I2C_CONTROLLER_DIRECTION_TX, 2);
    //主机开始发送，指定IIC外设、从机地址、读写方向（写方向）、数据长度（包含寄存器地址）
    // This function will send Start + Stop automatically.
    while (DL_I2C_getControllerStatus(IIC_OLED) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS)
        ;
}
#endif

#ifndef MYLIB_DEVICE_OLED_H
#    define MYLIB_DEVICE_OLED_H

#    include <mylib/mylib_config.h>

// 字库文件
#    include <mylib/device/oled_font.h>

// 缓冲区模式，先绘制在缓冲区上，再一次性用更新函数写入oled
#    define MYLIB_USE_OLED_BUFFER_MODE 1
// 直接模式，直接向oled写数据，不使用缓冲区
#    define MYLIB_USE_OLED_DIRECT_MODE 0

// oled的默认设备地址
#    define OLED_DEVICE_DEFAULT_ADDRESS 0x3C   // 未左移的7位原地址0011 1100

typedef struct
{
    uint8_t dev_addr;   // 设备从机地址
    // 将一个字节的data数据写入到从机设备的函数指针，从机地址为dev_addr，从机寄存器地址为reg_addr
    void (*iic_write_byte_func)(uint8_t dev_addr, uint8_t reg_addr, uint8_t data);
#    if MYLIB_USE_OLED_BUFFER_MODE
    // oled的缓冲区，用于存储要显示的内容
    uint8_t buffer[8][128];
#    endif
} oled_t;

// oled初始化
void oled_init(oled_t* oled);
// 设置oled光标
void oled_set_cursor(oled_t* oled, uint8_t page, uint8_t x);

#    if MYLIB_USE_OLED_BUFFER_MODE
// 缓冲区模式下，下面这些函数才有效
///////////////////////////////////////////////////////////////////////////////
// 更新函数，将缓冲区内容写入到oled设备
void oled_update(oled_t* oled);
// 更新部分区域
void oled_update_area(oled_t* oled, uint8_t x, uint8_t y, uint8_t widtht, uint8_t height);
// 清空缓冲区
void oled_clear(oled_t* oled);
// 清空缓冲区指定区域
void oled_clear_area(oled_t* oled, uint8_t x, uint8_t y, uint8_t widtht, uint8_t height);
// 翻转缓冲区
void oled_reverse(oled_t* oled);
// 翻转缓冲区指定区域
void oled_reverse_area(oled_t* oled, uint8_t x, uint8_t y, uint8_t widtht, uint8_t height);
#    elif MYLIB_USE_OLED_DIRECT_MODE
// 清空缓存区
void oled_clear(oled_t* oled);
// 清空line1到line2之间的内容，行
void oled_clear_line(oled_t* oled, uint8_t line1, uint8_t line2);
// 清空column1到column2之间的内容，列
void oled_clear_column(oled_t* oled, uint8_t column1, uint8_t column2);
#    endif
///////////////////////////////////////////////////////////////////////////////
// 绘制函数

// 绘制一个字符
void oled_draw_char(oled_t* oled, uint8_t line, uint8_t column, char ch);
// 在单行之内，绘制字符串
void oled_draw_string(oled_t* oled, uint8_t line, uint8_t column, char* str);
// 在多行之间，绘制字符串
void oled_draw_string_from_line_to_line(oled_t* oled, uint8_t startline, char* str,
                                        uint8_t endline);
// 从一个点开始，多行之间，绘制字符串
void oled_draw_string_from_point_to_line(oled_t* oled, uint8_t line, uint8_t column, char* str,
                                         uint8_t endline);
// 绘制整数
void oled_draw_number(oled_t* oled, uint8_t line, uint8_t column, uint32_t number, uint8_t length);
// 绘制带符号的整数
void oled_draw_signed_number(oled_t* oled, uint8_t line, uint8_t column, int32_t number,
                             uint8_t length);
// 绘制十六进制的整数
void oled_draw_hex_number(oled_t* oled, uint8_t line, uint8_t column, uint32_t number,
                          uint8_t length);
// 绘制二进制的整数
void oled_draw_bin_number(oled_t* oled, uint8_t line, uint8_t column, uint32_t number,
                          uint8_t length);

// 绘制形状的函数仅在缓冲模式下有效
#    if MYLIB_USE_OLED_BUFFER_MODE
// 绘制一个点
void oled_draw_point(oled_t* oled, uint8_t x, uint8_t y);
// 获取一个点的值
uint8_t oled_get_point(oled_t* oled, uint8_t x, uint8_t y);
// 绘制一条直线
void oled_draw_line(oled_t* oled, uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1);
// 绘制一个矩形
void oled_draw_rectangle(oled_t* oled, uint8_t x, uint8_t y, uint8_t widtht, uint8_t height,
                         bool is_filled);
// 绘制一个三角形
void oled_draw_triangle(oled_t* oled, uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t x2,
                        uint8_t y2, bool is_filled);
// 绘制一个圆
void oled_draw_circle(oled_t* oled, uint8_t x, uint8_t y, uint8_t radius, bool is_filled);
// 绘制一个椭圆
void oled_draw_ellipse(oled_t* oled, uint8_t x, uint8_t y, uint8_t a, uint8_t b, bool is_filled);
// 绘制一个弧
void oled_draw_arc(oled_t* oled, uint8_t x, uint8_t y, uint8_t radius, int16_t start_angle,
                   int16_t end_angle, bool is_filled);
#    endif

#endif   // !MYLIB_DEVICE_OLED_H
